try
{
    const LocalRowTag =
        TR({class: "$member.upDownRow memberRow $member.open $member.styleClassName\\Row", $hasChildren: "$member.hasChildren",
            level: "$member.level", isTestFire: "$member.isTestFire", _associatedObject: "$member.associatedObject"},
            TD({class: "memberLabelCell", style: "padding-left: $member.indent\\px"},
                DIV({class: "memberLabel $member.styleClassName\\Label"},
                    "$member.name")
                ),
            TD({class: "memberValueCell"},
                TAG("$member.tag", {object: "$member.value"})
                )
            );
    var OBJECTLINK = FBL.OBJECTLINK =
                     A({
                         class: "objectLink objectLink-$className",
                         _repObject: "$object"
                     });

    /**
     * The base 'class' that describes a TestFireObject.
     */
    TestFireObject = {
        displayValue: "Test",
        isTestFireObject: 1,
        parent: null,
        editable: 0,
        selectable: 0,
        deletable: 0,

    /**
     * Should be called by the constructor of each and every object that extends This.
     */
        doInit: function( parent )
        {
            this.upDownEnabled = false;
            this.parent = parent;
            this.displayValue = "Test";
            this.init.apply(this, arguments);
        },

    /**
     *  To be overridden if needed. Will be called with all arguments passed to doInit
     */
        init : function()
        {
        },

    /**
     *  Should/can be overridden, but provides a useful default.
     *  getMembers returns nothing, but populates a list of objects which
     *  represent the children of this object in the tree.
     *  @todo update below comments
     *  @memberStore An object with child objects, as per TestFireTreePanel.js#(something)
     *  @level  The current depth in the tree.
     *  @doAddMemberPtr A pointer to the method in the (something), in TestFireTreePanel.js
     */
        getMembers: function( memberStore, level, doAddMemberPtr )
        {

            try
            {
                var ary = this.getChildrenToDisplay();
                for ( var i = 0; i < ary.length; ++i )
                {
                    doAddMemberPtr(ary[i].target ? ary[i].target
                        : this, ary[i].name, memberStore, level, ary[i].object);
                }
            }
            catch( ex )
            {
                Firebug.TestFire.util.tf_debug(ex);
            }

        },

    open : function(element, parent)
    {
        alert("Open not implemented for " + element.name);
    },

    /**
     * Get all displayable children and their names in an array of objects. The returned values will be displayed
     * in the order returned.
     * @return An array containing objects of the form: {name: 'name', object: this[name]} .
     */
        getChildrenToDisplay: function()
        {
            var ary = new Array();
            try
            {
                var x = TestFireObject;
                for ( var name in this )
                {
                    if ( !(name in x) ) // prevent global variables from showing in the tree.
                    {
                        ary.push({name: name, object: this[name]});
                    }
                }
            }
            catch( ex )
            {
                Firebug.TestFire.util.tf_debug(ex);
            }

            return ary;
        },


    /**
     * A simple default.
     * @return if this has displayable children or not.
     */
        getHasChildren: function()
        {
            return this.getChildrenToDisplay().length != 0;
        },

        
    /**
     * Overrides toString in Object.
     */
        toString : function()
        {
            return this.displayValue;
        },

    /**
     *  To be overridden always.
     *  Output the Schema conformant representation of the child object that is 'extending' this TestFireObject.
     */
        toXMLStringAry: function()
        {
            return new Array()
        },


    /**
     *
     */
        getValue: function()
        {
            return this.displayValue;
        },








    /**
     * Get the Menu items.
     */
        _getContextMenuItems: function()
        {
            var items = [];
            if ( this.deletable )
            {
                items.push("-", { nol10n: 1, label: "Delete", command: FBL.bindFixed(this.deleteSelf, this) });
            }
            return items;
        },
        getContextMenuItems: function( object, target )
        {
            return this._getContextMenuItems(object, target);
        },


    /**
     *
     */
        printMsg: function( msg )
        {
            Firebug.TestFire.util.tf_debug(msg);
        },


        rowRep: LocalRowTag,

        isUpDownEnabled : function()
        {
               return this.upDownEnabled;
        },

        isUpAvailable : function()
        {
            return false;
        },

        isDownAvailable : function()
        {
            return false;
        },

        /**
         * Check 'this' against the passed array of ordered siblings to determine if this object can be moved up.
         * @param siblingsArray The siblings of this, as displayed and possibly stored ( changing display order without
         * changing store order would likely break something).
         */
        _isUpAvailable : function(siblingsArray)
        {
            return siblingsArray.length > 1 && siblingsArray[0] != this;
        },

        /**
         * Check 'this' against the passed array of ordered siblings to determine if this object can be moved down.
         * @param siblingsArray The siblings of this, as displayed and possibly stored ( changing display order without
         * changing store order would likely break something).
         */
        _isDownAvailable : function(siblingsArray)
        {
            return siblingsArray.length > 1 && siblingsArray[siblingsArray.length - 1] != this;
        },

        /**
         * Move 'this' down with respect to its position in the passed array of ordered siblings.
         * @param siblingsArray The siblings of this, as displayed and possibly stored ( changing display order without
         * changing store order would likely break something).
         */
        _moveDown : function(siblingsArray)
        {
            var L = siblingsArray.length;
            var prev = siblingsArray[L - 1];
            for ( var i = L - 2; i >= 0; --i )
            {
                if ( siblingsArray[i] == this )
                {
                    siblingsArray[i + 1] = this;
                    siblingsArray[i] = prev;
                    break;
                }
                else
                {
                    prev = siblingsArray[i];
                }
            }
            Firebug.TestFire.refreshAll();
        },

        /**
         * Move 'this' down with respect to its position in the passed array of ordered siblings.
         * @param siblingsArray The siblings of this, as displayed and possibly stored ( changing display order without
         * changing store order would likely break something).
         */
        _moveUp : function(siblingsArray)
        {
            var prev = siblingsArray[0];
            for ( var i = 1, L = siblingsArray.length; i < L; ++i )
            {
                if ( siblingsArray[i] == this )
                {
                    siblingsArray[i - 1] = this;
                    siblingsArray[i] = prev;
                    break;
                }
                else
                {
                    prev = siblingsArray[i];
                }
            }
            Firebug.TestFire.refreshAll();
        },


        getMember: function( styleClassName, props, name, level, order, tag )
        {
            return {
                name: this.displayValue,
                value: this,
                styleClassName: styleClassName,
                rowClass: "memberRow-" + styleClassName,
                open: "",
                order: order,
                level: level,
                indent: level * 16,
                hasChildren: this.getHasChildren(),
                isTestFire : 1,
                tag: tag,
                upDownRow: this.isUpDownEnabled() ? 'upDownRow' : '',
                associatedObject: this
            }
        },

        

        deleteChild: function( child )
        {
            for ( var x in this )
            {
                if ( this[x] === child )
                {
                    delete this[x];
                }
            }
        },


    /**
     * Utility functions
     */


        deleteFromArray : function(item, array)
        {
            var ary = new Array();
            for(var i = 0, L = array.length; i < L; ++i)
            {
                if(array[i] != item)
                {
                    ary.push(array[i]);
                }
            }            
            return ary;
        },

        getFirstChildElement : function(element)
            {
                var childEls = element.childNodes;
                for(var i = 0, L = childEls.length; i < L; i++){
                    if(childEls[i].nodeType == element.ELEMENT_NODE)
                    {
                        return childEls[i];
                    }
                }
            },

            getLastChildElement : function(element)
            {
                var childEls = element.childNodes;
                var L = childEls.length;
                for(var i = L-1; i >=0; --i){
                    if(childEls[i].nodeType == element.ELEMENT_NODE)
                    {
                        return childEls[i];
                    }
                }
            },
        
         /**
         * copy the list of attributes onto the object as property values.
         * @param obj The object.
         * @param attrs A list of xml attributes, as specified by the DOM.
         */
        copyAttributesToObject : function(obj, attrs)
        {
            if(obj == null || attrs == null || attrs.length ==0 )
            {
                return;
            }
            for ( var i = 0, L = attrs.length; i < L; ++i )
            {
                try{
                    if(attrs[i].name){
                        if(attrs[i].name.length > 4 && attrs[i].name.substring(0, 5) == 'xmlns')
                        {
                            continue;
                        }
                        obj[attrs[i].name] = attrs[i].value;
                    }
                }
                catch(ex)
                {
                    // @todo this is just here for curiosity; not sure if we have attrs that will throw up or not.
                    Firebug.TestFire.util.tf_debug(ex);
                }
            }
        },
        
        addAttrs: function( str, attrs )
        {
            for ( var name in attrs )
            {
                var attr = this.escapeQuotes(attrs[name]);
                str += " " + name + '="' + attr + '"';
            }
            return str
        },
        xmlEncode: function ( value )
        {
            if ( !value )
                return value;
            else
                return String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
        },


        escapeQuotes: function ( str )
        {
            return this.xmlEncode(str);
        },
        escapeHTML: function( str )
        {
            return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        }








    }
}
catch( e )
{
    Firebug.TestFire.util.tf_debug(e);
}